import random
from abc import abstractmethod
from typing import Callable

from sam import import_dir_path, import_csv_path, import_excel_path, import_txt_path
from sam.util import csvUtil, excelUtil, txtUtil
from sam.util.convertUtil import list2dict
from sam.util.excelUtil import split_sheet_2_file
from sam.util.fileUtil import get_file_path_list_by_dir_path, setup_export_file_path, get_file_name_from_path
from sam.util.logUtil import LoggerUtil
from sam.util.strUtil import pre_handle_str


def read_file(file_path: str
              , is_skip_first_line: bool = False
              , optional: str = "list"
              , column: list = None
              , result_formatter: Callable[[list, list], list] = None
              , start: int = 0
              , end: int = None
              , max_random_count: int = None
              ):
    """
    读取文件:
    1: 支持的文件格式有[ .csv .xls .xlsx .log .txt]
    2: 支持对读取的结果格式化
    3: 支持随机读取，主要用于判断文件格式的是否一致
    4: 支持跳过第一行, 默认是不跳过
    5: 支持读取指定的起止区间
    """
    if optional not in ["list", "dict"]:
        raise RuntimeError(f"不支持 当前的可选项 {optional}, 仅支持 list, dict 以及自定义的 formatter")
    if optional == "dict":
        if not column:
            raise RuntimeError(f"当你期望读取结果格式化为字典时,请务必填写 column 参数")

    if file_path.endswith(".csv") or file_path.endswith(".xlsx") or file_path.endswith(".xls") or file_path.endswith(
            ".log") or file_path.endswith(".txt"):
        if file_path.endswith(".csv"):
            if is_skip_first_line:
                read_result = csvUtil.csv_read(file_path)[1:]
            else:
                read_result = csvUtil.csv_read(file_path)
        elif file_path.endswith(".log") or file_path.endswith(".txt"):
            with open(file_path, "r", encoding="utf-8") as f:
                read_result = f.readlines()
        else:
            read_result = excelUtil.excel_read_single_sheet(file_path, is_skip_first_line=is_skip_first_line)

        end = end if end else len(read_result)
        end = end if len(read_result) >= end else len(read_result)
        need_handle_result = read_result[start: end]
        random_read_result = []
        if max_random_count and max_random_count > 0:
            max_random_count = len(need_handle_result) if len(
                need_handle_result) <= max_random_count else max_random_count
            for i in range(max_random_count):
                if i == 0:
                    random_read_result.append(need_handle_result[0])
                else:
                    random_index = random.randint(1, max_random_count - 1)
                    random_read_result.append(need_handle_result[random_index])

        need_handle_result = random_read_result if random_read_result else need_handle_result
        if result_formatter:
            # 如果 自定义了 格式化方法 就不走 系统预设的结果格式化方法
            return result_formatter(need_handle_result, column)
        else:
            if "list" == optional:
                return need_handle_result
            else:
                new_read_result = []
                for res in need_handle_result:
                    new_read_result.append(list2dict(column, res))
                return new_read_result

    else:
        raise RuntimeError(f"不支持 对 当前文件 {file_path} 的 读写 ")


def read_file_quick(file_type: str):
    if file_type == 'csv':
        file_path = import_csv_path
    elif file_type == 'xlsx':
        file_path = import_excel_path
    elif file_type == 'txt':
        file_path = import_txt_path
    else:
        msg = f"不支持 对 当前文件 {file_type} 的 读取 "
        raise RuntimeError(msg)
    line_list = read_file(file_path)
    if file_type == "txt":
        return [line.strip() for line in line_list if line.strip()]
    else:
        return [line[0].strip() for line in line_list if line[0].strip()]


def write_file(file_path: str, data_list: list, column: list = None, optional="cover"):
    if file_path.endswith(".csv"):
        csvUtil.csv_write(file_path, data_list, column=column, optional=optional)
    elif file_path.endswith(".xlsx") or file_path.endswith(".xls"):
        excelUtil.excel_write(file_path, data_list, column=column, optional=optional)
    elif file_path.endswith(".txt"):
        txtUtil.txt_write(file_path, data_list, column=column, optional=optional)
    else:
        msg = f"不支持 对 当前文件 {file_path} 的 读写 "
        raise RuntimeError(msg)


def write_file_quick(data_list: list
                     , export_file_name: str = "quick_export"
                     , column: list = None
                     , prefix: str = None
                     , suffix: str = None
                     , file_type: str = "csv"
                     , optional="cover"):
    export_file_path = setup_export_file_path(export_file_name=export_file_name, prefix=prefix, suffix=suffix,
                                              file_type=file_type)
    if export_file_path.endswith(".csv"):
        csvUtil.csv_write(export_file_path, data_list, column=column, optional=optional)
    elif export_file_path.endswith(".xlsx") or export_file_path.endswith(".xls"):
        excelUtil.excel_write(export_file_path, data_list, column=column, optional=optional)
    elif export_file_path.endswith(".txt"):
        txtUtil.txt_write(export_file_path, data_list, column=column, optional=optional)
    else:
        msg = f"不支持 对 当前文件 {export_file_path} 的 读写 "
        raise RuntimeError(msg)


def write_multi_sheet_excel_quick(
        sheet_name_content_map: dict
        , export_file_name: str = "quick_export"
):
    export_file_path = setup_export_file_path(export_file_name=export_file_name, file_type="xlsx")
    excelUtil.excel_write_multi_sheet_cover(sheet_name_content_map, export_file_path)


def check_file(file_path: str, import_table_column_list: list = None):
    if import_table_column_list:
        print(f"将对文件: {file_path} 的内容进行检查...")
        # lines = read_file(file_path, max_random_count=max_random_count, is_skip_first_line=False)
        lines = read_file(file_path, is_skip_first_line=False)
        if lines:
            column_line_str = ''.join(lines[0])
            table_column_str = ''.join(import_table_column_list)
            if column_line_str.lower() == table_column_str.lower():
                column_length = len(import_table_column_list)
                for line in lines:
                    if line and column_length == len(line):
                        continue
                    else:
                        print(f"记录: {line} 长度不够!")
                        print(f"注意: 文件: {file_path} 的内容检查不通过")
                        return False
                print(f"文件: {file_path} 的内容检查通过")
                return True
            else:
                print(f"注意: 文件: {file_path} 的内容检查不通过")
                return False
        else:
            print(f"文件:{file_path} 表头与预设定的表头不一致")
            msg = f"文件: {file_path} 读取到的内容为空."
            raise RuntimeError(msg)
    else:
        print(f"注意: 没有对文件: {file_path} 的内容进行检查.")
        return True


class FileUtil(LoggerUtil):
    def __init__(self, base_dir_path: str = import_dir_path, import_table_column_list: list = None):
        super().__init__(name="HandFile", level="info")
        self.base_dir_path = base_dir_path
        self.import_table_column_list = import_table_column_list

    @staticmethod
    def extract_file_name_from_path(file_path: str, with_file_type: bool = False) -> str:
        file_name_with_type = get_file_name_from_path(file_path)
        if with_file_type:
            return file_name_with_type
        return file_name_with_type.split(".")[0]

    def assert_read_file_content_is_suitable(self, file_path) -> bool:
        if self.import_table_column_list:
            self.logger.info(f"将对文件: {file_path} 的内容进行检查...")
            lines = read_file(file_path, is_skip_first_line=False)
            if lines:
                column_line_str = ''.join(lines[0])
                column_line_str = pre_handle_str(column_line_str)
                table_column_str = ''.join(self.import_table_column_list)
                table_column_str = pre_handle_str(table_column_str)
                if column_line_str.lower() == table_column_str.lower():
                    column_length = len(self.import_table_column_list)
                    for line in lines:
                        if line and column_length == len(line):
                            continue
                        else:
                            self.logger.error(f"记录: {line} 长度不够!")
                            self.logger.error(f"注意: 文件: {file_path} 的内容检查不通过")
                            return False
                    self.logger.info(f"文件: {file_path} 的内容检查通过")
                    return True
                else:
                    self.logger.error(f"注意: 文件: {file_path} 的内容检查不通过")
                    return False
            else:
                self.logger.error(f"文件:{file_path} 表头与预设定的表头不一致")
                msg = f"文件: {file_path} 读取到的内容为空."
                raise RuntimeError(msg)
        else:
            self.logger.warn(f"注意: 没有对文件: {file_path} 的内容进行检查.")
            return True

    @staticmethod
    def setup_export_file_path(file_path
                               , prefix=None
                               , suffix=None
                               , file_type: str = "csv") -> str:
        export_file_name = get_file_name_from_path(file_path)
        if "." in export_file_name:
            export_file_name = export_file_name[:export_file_name.find(".")]
        return setup_export_file_path(export_file_name=export_file_name, prefix=prefix, suffix=suffix,
                                      file_type=file_type)

    def _batch_handle(self, handle_single_file_func: Callable[[str], None], start: int = None, end: int = None):
        if not self.base_dir_path:
            raise RuntimeWarning("基本路径不能为空")
        handle_file_path_lit = get_file_path_list_by_dir_path(self.base_dir_path)
        if handle_file_path_lit:
            max_size = len(handle_file_path_lit)
            start_index = start if start else 0
            if not end:
                end_index = 0 if end == 0 else max_size - 1
            else:
                end_index = end
            for i in range(max_size):
                if start_index <= i <= end_index:
                    file_path = handle_file_path_lit[i]
                    self.logger.info(f"开始处理 文件: {file_path}  ...")
                    handle_single_file_func(file_path)
                    self.logger.info(f"处理文件: {file_path} 结束")

        else:
            raise RuntimeWarning("该文件夹是空文件夹")

    def handle_dir(self, start: int = None, end: int = None):
        self._batch_handle(self.handle_file, start=start, end=end)
        self.end_handle()

    @abstractmethod
    def handle_file(self, file_path: str):
        pass

    @staticmethod
    def file_read(file_path: str
                  , is_skip_first_line: bool = False
                  , optional: str = "list"
                  , column: list = None
                  , result_formatter: Callable[[list, list], list] = None
                  , start: int = None
                  , end: int = None
                  , max_random_count: int = None
                  ):
        return read_file(
            file_path=file_path
            , is_skip_first_line=is_skip_first_line
            , optional=optional
            , column=column
            , result_formatter=result_formatter
            , start=start
            , end=end
            , max_random_count=max_random_count
        )

    @staticmethod
    def column_read(file_path: str):
        return read_file(
            file_path=file_path
            , is_skip_first_line=False
            , optional="list"
            , start=0
            , end=1
        )

    @staticmethod
    def file_write(file_path: str, data_list: list, column: list = None, optional: str = "cover"):
        write_file(file_path=file_path, data_list=data_list, column=column, optional=optional)

    def file_write_quick(self, file_path: str = None
                         , data_list: list = None
                         , export_file_name: str = None
                         , column: list = None
                         , prefix=None
                         , suffix=None
                         , optional: str = "cover"
                         , file_type: str = "csv"
                         ):
        if export_file_name:
            write_file_quick(data_list=data_list
                             , export_file_name=export_file_name
                             , column=column
                             , prefix=prefix
                             , suffix=suffix
                             , optional=optional
                             , file_type=file_type)
        else:
            export_file_path = self.setup_export_file_path(file_path, prefix=prefix, suffix=suffix, file_type=file_type)
            write_file(file_path=export_file_path
                       , data_list=data_list
                       , column=column
                       , optional=optional
                       )

    @staticmethod
    def split_file_sheet(original_file_path: str):
        split_sheet_2_file(original_file_path)

    def end_handle(self):
        pass


class NotSupportFileTypeError(Exception):
    def __init__(self, msg: str = "文件类型不支持异常"):
        super().__init__()
        self.msg = msg

    def __str__(self):
        return repr(self.msg)


class FileBatchHandleUtil(object):

    def file_handle(self, file_path: str, is_skip_first_line=True, batch_size=100):
        lines = read_file(file_path, is_skip_first_line=is_skip_first_line)
        line_list = []
        for index, line in enumerate(lines):
            # 编码汇总	系统名称	省份	城市	县域	地址
            if index > 0 and index % batch_size == 0:
                self.batch_handle_func(line_list)
                line_list.clear()
            line_list.append(line)
        else:
            self.batch_handle_func(line_list)
            line_list.clear()

    @abstractmethod
    def batch_handle_func(self, line_list: list):
        pass


class Combine(FileUtil):
    """
    合并文件
    city, term, property
    """

    def __init__(self, dir_path, prefix="全量数据", suffix=""):
        super().__init__()
        self.export_csv_path = self.setup_export_file_path(dir_path, prefix=prefix, suffix=suffix)

    def handle_file(self, file_path: str):
        new_lines = self.file_read(file_path, is_skip_first_line=False)
        self.file_write(self.export_csv_path, new_lines, optional="append")


class Csv2Xlsx(FileUtil):
    """
    csv 转换成 excel
    """

    def handle_file(self, file_path: str):
        lines = self.file_read(file_path)
        export_file_path = self.setup_export_file_path(file_path, file_type="xlsx")
        self.file_write(export_file_path, lines)

    def csv2xlsx(self):
        self.handle_dir()


class Xlsx2Csv(FileUtil):

    def handle_file(self, file_path: str):
        lines = self.file_read(file_path)
        export_file_path = self.setup_export_file_path(file_path)
        self.file_write(export_file_path, lines)

    def xlsx2csv(self):
        self.handle_dir()
